package com.starleague.hexagonal.flat;

import java.text.DecimalFormat;
import java.util.LinkedList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.starleague.hexagonal.IHexCoordinate;
import com.starleague.hexagonal.IHexGridUtil;
import com.starleague.hexagonal.ImmutableHexCoordinate;

// TODO: Should calculations be in Cartesian Coordinates, where [0,0] is in the bottom-left corner?
// Battletech has 0,0 be in the upper-left corner, and has Y values increase (positively) as you go down
// NOTE: For now, do ALL calculations for Battletech!!

public class FlatHexGridUtil
        implements IHexGridUtil
{
    private static final Logger log = LoggerFactory.getLogger(FlatHexGridUtil.class);

    private static final double ANGLE_60 = Math.PI / 3.0;
    private static final double ANGLE_120 = 2.0 * Math.PI / 3.0;
    private static final double ANGLE_180 = 3.0 * Math.PI / 3.0;
    private static final double ANGLE_240 = 4.0 * Math.PI / 3.0;
    private static final double ANGLE_300 = 5.0 * Math.PI / 3.0;
    private static final double ANGLE_360 = 2.0 * Math.PI;
    private static final double PRECISION = 0.000001;

    /**
     * Hex radius (made up; does not correspond to any actual Hexes)
     */
    private static final double HEX_RADIUS = 100.0;

    /**
     * Height from center of hex
     */
    private static final double HEX_HALF_HEIGHT = HEX_RADIUS / Math.tan(Math.PI / 6.0) / 2.0;
    // private static final double HEX_HALF_HEIGHT = (HEX_RADIUS * Math.sqrt(3))
    // / 2.0;

    /**
     * 1/2 of hex radius
     */
    private static final double HEX_QUARTER_WIDTH = HEX_RADIUS / 2.0;

    private static final DecimalFormat df = new DecimalFormat("#.########");

    public IHexCoordinate getHexCoordinateInDirection(IHexCoordinate hexCoordinate,
                                                      FlatHexDirection flatHexDirection)
    {
        final int gridX = hexCoordinate.getGridX();
        final int gridY = hexCoordinate.getGridY();

        switch (flatHexDirection)
        {
            case SOUTHEAST:
                return (isAboveSameGridY(gridX))
                        ? new ImmutableHexCoordinate(gridX + 1, gridY)
                        : new ImmutableHexCoordinate(gridX + 1, gridY + 1);

            case NORTHEAST:
                return (isAboveSameGridY(gridX))
                        ? new ImmutableHexCoordinate(gridX + 1, gridY - 1)
                        : new ImmutableHexCoordinate(gridX + 1, gridY);

            case SOUTHWEST:
                return (isAboveSameGridY(gridX))
                        ? new ImmutableHexCoordinate(gridX - 1, gridY)
                        : new ImmutableHexCoordinate(gridX - 1, gridY + 1);

            case NORTHWEST:
                return (isAboveSameGridY(gridX))
                        ? new ImmutableHexCoordinate(gridX - 1, gridY - 1)
                        : new ImmutableHexCoordinate(gridX - 1, gridY);

            case NORTH:
                return new ImmutableHexCoordinate(gridX, gridY - 1);

            case SOUTH:
                return new ImmutableHexCoordinate(gridX, gridY + 1);
        }

        // Major error: New Direction?!?!
        throw new RuntimeException("Invalid FlatHexDirection: " + flatHexDirection);
    }

    public FlatHexDirection[] getFlatHexDirections(IHexCoordinate srcCoordinate,
                                                   IHexCoordinate destCoordinate)
    {
        final double deltaX = calcCenterX(destCoordinate) - calcCenterX(srcCoordinate);
        final double deltaY = calcCenterY(destCoordinate) - calcCenterY(srcCoordinate);

        double radians = Math.atan(deltaY / deltaX);
        if (deltaX < 0)
            radians -= Math.PI;
        radians = (radians + (Math.PI * 2)) % (Math.PI * 2);

        log.trace("CALC_DIR: [{}] [{}] dx [{}] dy [{}] rad [{}] deg [{}]",
                  srcCoordinate,
                  destCoordinate,
                  df.format(deltaX),
                  df.format(deltaY),
                  df.format(radians),
                  df.format(radians * 180.0 / Math.PI));

        // Angle starts at 0 == EAST, and curves clockwise
        //
        // Always return the counterclockwise value then the clockwise value
        if (radians < PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.NORTHEAST, FlatHexDirection.SOUTHEAST };
        if (radians <= ANGLE_60 - PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.SOUTHEAST };
        if (radians < ANGLE_60 + PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.SOUTHEAST, FlatHexDirection.SOUTH };
        if (radians <= ANGLE_120 - PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.SOUTH };
        if (radians < ANGLE_120 + PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.SOUTH, FlatHexDirection.SOUTHWEST };
        if (radians <= ANGLE_180 - PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.SOUTHWEST };
        if (radians < ANGLE_180 + PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.SOUTHWEST, FlatHexDirection.NORTHWEST };
        if (radians <= ANGLE_240 - PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.NORTHWEST };
        if (radians < ANGLE_240 + PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.NORTHWEST, FlatHexDirection.NORTH };
        if (radians <= ANGLE_300 - PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.NORTH };
        if (radians < ANGLE_300 + PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.NORTH, FlatHexDirection.NORTHEAST };
        if (radians <= ANGLE_360 - PRECISION)
            return new FlatHexDirection[] { FlatHexDirection.NORTHEAST };
        // >= 360 will wrap around to 0
        return new FlatHexDirection[] { FlatHexDirection.NORTHEAST, FlatHexDirection.SOUTHEAST };
    }

    // public boolean intersectsHex(IHexCoordinate srcCoordinate,
    // IHexCoordinate destCoordinate,
    // IHexCoordinate hexCoordinate)
    // {
    // return intersectsHex(calcCenterX(srcCoordinate),
    // calcCenterY(srcCoordinate),
    // calcCenterX(destCoordinate),
    // calcCenterY(destCoordinate),
    // hexCoordinate);
    // }

    @Override
    public List<IHexCoordinate> calculateLineOfSightPath(IHexCoordinate srcCoordinate,
                                                         IHexCoordinate destCoordinate)
    {
        log.debug("SRC : {}", srcCoordinate);
        log.debug("DEST: {}", destCoordinate);

        final double srcCenterX = calcCenterX(srcCoordinate);
        final double srcCenterY = calcCenterY(srcCoordinate);
        final double destCenterX = calcCenterX(destCoordinate);
        final double destCenterY = calcCenterY(destCoordinate);

        FlatHexDirection directions[] = getFlatHexDirections(srcCoordinate, destCoordinate);
        // 1 == passes through only 1 side
        // 2 == passes through between 2 hexes equally (IE: an exact shared side
        // of 2 hexes)
        if (directions.length == 1)
        {
            // Add left and right sides before straight side (or you might miss
            // a side!)
            directions = new FlatHexDirection[] { directions[0].getLeft(),
                                                 directions[0].getRight(),
                                                 directions[0] };
        }

        log.debug("SEGMENT [{}] [{}] - [{}] [{}] Directions [{}]",
                  df.format(srcCenterX),
                  df.format(srcCenterY),
                  df.format(destCenterX),
                  df.format(destCenterY),
                  directions);

        log.debug("PATH: {}", srcCoordinate);

        final List<IHexCoordinate> path = new LinkedList<IHexCoordinate>();
        path.add(srcCoordinate);

        IHexCoordinate currentCoordinate = srcCoordinate;
        while (!currentCoordinate.equals(destCoordinate))
        {
            IHexCoordinate nextCoordinate = null;
            for (FlatHexDirection flatHexDirection : directions)
            {
                final IHexCoordinate tmpCoordinate = getHexCoordinateInDirection(currentCoordinate,
                                                                                 flatHexDirection);
                if (intersectsHex(srcCenterX, srcCenterY, destCenterX, destCenterY, tmpCoordinate))
                {
                    if (nextCoordinate != null)
                        log.debug("CHOICE: [{}] or [{}]", tmpCoordinate, nextCoordinate);
                    else
                        nextCoordinate = tmpCoordinate;
                }
            }

            if (nextCoordinate == null)
                throw new RuntimeException("Path not found at: " + currentCoordinate);

            currentCoordinate = nextCoordinate;
            path.add(currentCoordinate);
            log.debug("PATH: {}", currentCoordinate);
        }

        return path;
    }

    /**
     * [x+1][y] is SOUTHEAST of [x][y] </p> IE: Even X coordinates are below odd
     * X coordinates </p>
     * 
     * @param gridX
     */
    public static boolean isAboveSameGridY(int gridX)
    {
        return (gridX & 1) == 1;
    }

    private enum SideFacing
    {
        LEFT,
        STRAIGHT,
        RIGHT
    }

    // TODO: Maybe different for Pointy?
    private static final SideFacing turns(final double x0,
                                          final double y0,
                                          final double x1,
                                          final double y1,
                                          final double x2,
                                          final double y2)
    {
        final double cross = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0);
        if (cross < -PRECISION)
            return SideFacing.RIGHT;
        if (cross > PRECISION)
            return SideFacing.LEFT;
        return SideFacing.STRAIGHT;
    }

    private double calcCenterX(IHexCoordinate hexCoordinate)
    {
        return (hexCoordinate.getGridX() * 3 + 2) * HEX_QUARTER_WIDTH;
    }

    private double calcCenterY(IHexCoordinate hexCoordinate)
    {
        // TODO: use isAboveSameGridY
        return (hexCoordinate.getGridY() * 2 + 1 + ((hexCoordinate.getGridX() + 1) % 2))
               * HEX_HALF_HEIGHT;
    }

    private double[][] calcPoints(IHexCoordinate hexCoordinate)
    {
        final double centerX = calcCenterX(hexCoordinate);
        final double centerY = calcCenterY(hexCoordinate);

        // log.info("CENTER [{}] [{}]", df.format(centerX), df.format(centerY));

        final double[][] points = new double[6][2];

        points[0][0] = centerX - HEX_QUARTER_WIDTH;
        points[0][1] = centerY - HEX_HALF_HEIGHT;

        points[1][0] = centerX + HEX_QUARTER_WIDTH;
        points[1][1] = centerY - HEX_HALF_HEIGHT;

        points[2][0] = centerX + HEX_QUARTER_WIDTH * 2.0;
        points[2][1] = centerY;

        points[3][0] = centerX + HEX_QUARTER_WIDTH;
        points[3][1] = centerY + HEX_HALF_HEIGHT;

        points[4][0] = centerX - HEX_QUARTER_WIDTH;
        points[4][1] = centerY + HEX_HALF_HEIGHT;

        points[5][0] = centerX - HEX_QUARTER_WIDTH * 2.0;
        points[5][1] = centerY;

        for (int i = 0; i < points.length; ++i)
        {
            // log.info("PTS [{}] [{}]", df.format(points[i][0]),
            // df.format(points[i][1]));
        }

        return points;
    }

    private boolean intersectsHex(double srcCenterX,
                                  double srcCenterY,
                                  double destCenterX,
                                  double destCenterY,
                                  IHexCoordinate hexCoordinate)
    {
        final double[][] points = calcPoints(hexCoordinate);

        final SideFacing side0 = turns(srcCenterX,
                                       srcCenterY,
                                       destCenterX,
                                       destCenterY,
                                       points[0][0],
                                       points[0][1]);
        log.trace("SIDE0: {}", side0);
        if (side0 == SideFacing.STRAIGHT)
        {
            log.debug("INTERSECT [true:0]: {}", hexCoordinate);
            return true;
        }

        for (int i = 1; i < 6; i++)
        {
            final SideFacing sideN = turns(srcCenterX,
                                           srcCenterY,
                                           destCenterX,
                                           destCenterY,
                                           points[i][0],
                                           points[i][1]);
            log.trace("SIDE{}: {}", i, sideN);

            if (sideN == SideFacing.STRAIGHT || sideN != side0)
            {
                log.debug("INTERSECT [true:{}]: {}", i, hexCoordinate);
                return true;
            }
        }

        log.debug("INTERSECT [false]: {}", hexCoordinate);
        return false;
    }
}
// public List<IHexCoordinate> calculateShortestPath(IHexCoordinate
// srcCoordinate,
// IHexCoordinate destCoordinate) throws HexException
// {
// if (srcCoordinate == null)
// throw new HexException("HexGrid.CalculateLOS(): srcHex is null");
//
// if (destCoordinate == null)
// throw new HexException("HexGrid.CalculateLOS(): destHex is null");
//
// if (this.hexArray2D[srcHex.getGridX()][srcHex.getGridY()] != srcHex)
// throw new HexException("HexGrid.CalculateLOS(): srcHex is not on HexGrid: " +
// srcHex);
//
// if (this.hexArray2D[destHex.getGridX()][destHex.getGridY()] != destHex)
// throw new HexException("HexGrid.CalculateLOS(): srcHex is not on HexGrid: " +
// destHex);
//
// //
// //
// //
//
// final List<HEX> retList = Lists.newArrayList();
//
// // TODO: possibly overkill for checking arguments...
// if (srcHex == destHex)
// {
// retList.add(srcHex);
// return retList;
// }
// //
// final double src_center_x = (srcHex.getGridX() * 3 + 2) * LOS_HEX_DIV_WIDTH;
// final double src_center_y = (srcHex.getGridY() * 2 + 1 + ((srcHex.getGridX()
// + 1) % 2)) * LOS_HEX_DIV_HEIGHT;
//
// final double dest_center_x = (destHex.getGridX() * 3 + 2) *
// LOS_HEX_DIV_WIDTH;
// final double dest_center_y = (destHex.getGridY() * 2 + 1 +
// ((destHex.getGridX() + 1) % 2)) * LOS_HEX_DIV_HEIGHT;
//
// final int min_x = (srcHex.getGridX() < destHex.getGridX()) ?
// srcHex.getGridX() : destHex.getGridX();
// int min_y = (srcHex.getGridY() < destHex.getGridY()) ? srcHex.getGridY() :
// destHex.getGridY();
//
// final int max_x = (srcHex.getGridX() > destHex.getGridX()) ?
// srcHex.getGridX() : destHex.getGridX();
// int max_y = (srcHex.getGridY() > destHex.getGridY()) ? srcHex.getGridY() :
// destHex.getGridY();
//
// if (srcHex.getGridY() == destHex.getGridY())
// {
// // Since grid hexes are staggered, we need to include [+1/-1] Y in
// // some cases
// --min_y;
// ++max_y;
// }
//
// // FIXME XXX: I think this is the wrong calculation!!! This looks like LOS
// calculation!
//
// // TODO: start from min_x, instead of 0, and calculate [i % 2] correctly!
// for (int j = min_y; j <= max_y; ++j)
// for (int i = min_x; i <= max_x; ++i)
// {
// final double x_center = (i * 3 + 2) * LOS_HEX_DIV_WIDTH;
// final double y_center = (j * 2 + 1 + ((i + 1) % 2)) * LOS_HEX_DIV_HEIGHT;
//
// // clockwise from [11 o'clock] to [1 o'clock] ...
// final double xarr[] = { x_center - LOS_HEX_DIV_WIDTH,
// x_center + LOS_HEX_DIV_WIDTH,
// x_center + LOS_HEX_DIV_WIDTH * 2,
// x_center + LOS_HEX_DIV_WIDTH,
// x_center - LOS_HEX_DIV_WIDTH,
// x_center - LOS_HEX_DIV_WIDTH * 2 };
//
// final double yarr[] = { y_center - LOS_HEX_DIV_HEIGHT,
// y_center - LOS_HEX_DIV_HEIGHT,
// y_center,
// y_center + LOS_HEX_DIV_HEIGHT,
// y_center + LOS_HEX_DIV_HEIGHT,
// y_center };
//
// // first see if it intersects the infinite line
// final int side1 = turns(src_center_x, src_center_y, dest_center_x,
// dest_center_y, xarr[0], yarr[0]);
// if (side1 == STRAIGHT)
// {
// retList.add(this.hexArray2D[i][j]);
// // DEBUG("STRAIGHT-1");
// continue;
// }
// for (int corner_indx = 1; corner_indx < 6; ++corner_indx)
// {
// int side2 = turns(src_center_x,
// src_center_y,
// dest_center_x,
// dest_center_y,
// xarr[corner_indx],
// yarr[corner_indx]);
// if (side2 == STRAIGHT || side2 != side1)
// {
// // if (side2==STRAIGHT)
// // DEBUG("STRAIGHT-2");
// retList.add(this.hexArray2D[i][j]);
// break;
// }
// }
// }
//
// return retList;
// }
// }

//
// // /*
// // @SuppressWarnings ("unchecked") // 6.0 or later, sometimes 5.0
// // public Grid(final int min_grid_x,
// // final int min_grid_y,
// // final int grid_width,
// // final int grid_height) throws Hex.Exception
// // {
// // if (grid_width <= 0)
// // throw new Hex.Exception("Hex.Grid(): grid_width [" + grid_width +
// // "] is invalid");
// // if (grid_height <= 0)
// // throw new Hex.Exception("Hex.Grid(): grid_height [" + grid_height +
// // "] is invalid");
// //
// // _min_grid_x = min_grid_x;
// // _min_grid_y = min_grid_y;
// // _grid_width = grid_width;
// // _grid_height = grid_height;
// //
// // _hex_hexnode_map = new HashMap<HEX, Hex.Grid<HEX>.Node>();
// //
// // _hexnode_array_2d = new Hex.Grid.Node[_grid_width][_grid_height];
// //
// // //ArrayList<Node>[] myarr = (ArrayList<Node>[])
// // //Array.newInstance(ArrayList.class,n);
// //
// //
// // //
// // // Now populate: this.hexnode_array_2d
// // //
// //
// // for (int i = 0; i < this.grid_width; ++i)
// // for (int j = 0; j < this.grid_height; ++j)
// // // create the Node
// // _hexnode_array_2d[i][j] = new Hex.Grid.Node(i, j);
// //
// // //
// // // Now populate the edges:
// // this.hexnode_array_2d[i][j]->_edge_hexnode_array[Direction = ...
// // //
// //
// // for (int i = 0; i < this.grid_width; ++i)
// // {
// // //if (i == 4) continue;
// // //if (i == 5) continue;
// //
// // boolean above_same_grid_y = ((i + this.min_grid_x) % 2) == 1; // [x+1][y]
// is
// // SOUTHEAST of [x][y]
// //
// // for (int j = 0; j < this.grid_height; ++j)
// // {
// // // create the Node
// // //_hexnode_array_2d[i][j] = new Hex.Grid.Node(i, j);
// //
// // // Do *EAST edges
// // if (i + 1 < this.grid_width)
// // if (above_same_grid_y)
// // {
// // _hexnode_array_2d[i][j]._edge_hexnode_array.put(Direction.SOUTHEAST,
// // this.hexnode_array_2d[i + 1][j]);
// // _hexnode_array_2d[i + 1][j]._edge_hexnode_array.put(Direction.NORTHWEST,
// // this.hexnode_array_2d[i][j]);
// //
// // if (j > 0)
// // {
// // _hexnode_array_2d[i][j]._edge_hexnode_array.put(Direction.NORTHEAST,
// // this.hexnode_array_2d[i + 1][j - 1]);
// // _hexnode_array_2d[i + 1][j -
// 1]._edge_hexnode_array.put(Direction.SOUTHWEST,
// // this.hexnode_array_2d[i][j]);
// // }
// // }
// // else // !above_same_grid_y
// // {
// // _hexnode_array_2d[i][j]._edge_hexnode_array.put(Direction.NORTHEAST,
// // this.hexnode_array_2d[i + 1][j]);
// // _hexnode_array_2d[i + 1][j]._edge_hexnode_array.put(Direction.SOUTHWEST,
// // this.hexnode_array_2d[i][j]);
// //
// // if (j + 1 < this.grid_height)
// // {
// // _hexnode_array_2d[i][j]._edge_hexnode_array.put(Direction.SOUTHEAST,
// // this.hexnode_array_2d[i + 1][j + 1]);
// // _hexnode_array_2d[i + 1][j +
// 1]._edge_hexnode_array.put(Direction.NORTHWEST,
// // this.hexnode_array_2d[i][j]);
// // }
// // }
// //
// // // Do *WEST edges
// // if (i > 0)
// // if (above_same_grid_y)
// // {
// // _hexnode_array_2d[i][j]._edge_hexnode_array.put(Direction.SOUTHWEST,
// // this.hexnode_array_2d[i - 1][j]);
// // _hexnode_array_2d[i - 1][j]._edge_hexnode_array.put(Direction.NORTHEAST,
// // this.hexnode_array_2d[i][j]);
// //
// // if (j > 0)
// // {
// // _hexnode_array_2d[i][j]._edge_hexnode_array.put(Direction.NORTHWEST,
// // this.hexnode_array_2d[i - 1][j - 1]);
// // _hexnode_array_2d[i - 1][j -
// 1]._edge_hexnode_array.put(Direction.SOUTHEAST,
// // this.hexnode_array_2d[i][j]);
// // }
// // }
// // else // !above_same_grid_y
// // {
// // _hexnode_array_2d[i][j]._edge_hexnode_array.put(Direction.NORTHWEST,
// // this.hexnode_array_2d[i - 1][j]);
// // _hexnode_array_2d[i - 1][j]._edge_hexnode_array.put(Direction.SOUTHEAST,
// // this.hexnode_array_2d[i][j]);
// //
// // if (j + 1 < this.grid_height)
// // {
// // _hexnode_array_2d[i][j]._edge_hexnode_array.put(Direction.SOUTHWEST,
// // this.hexnode_array_2d[i - 1][j + 1]);
// // _hexnode_array_2d[i - 1][j +
// 1]._edge_hexnode_array.put(Direction.NORTHEAST,
// // this.hexnode_array_2d[i][j]);
// // }
// // }
// //
// // // Do NORTH edge
// // if (j > 0)
// // {
// // _hexnode_array_2d[i][j]._edge_hexnode_array.put(Direction.NORTH,
// // this.hexnode_array_2d[i][j - 1]);
// // _hexnode_array_2d[i][j - 1]._edge_hexnode_array.put(Direction.SOUTH,
// // this.hexnode_array_2d[i][j]);
// // }
// //
// // // Do SOUTH edge
// // if (j + 1 < this.grid_height)
// // {
// // _hexnode_array_2d[i][j]._edge_hexnode_array.put(Direction.SOUTH,
// // this.hexnode_array_2d[i][j + 1]);
// // _hexnode_array_2d[i][j + 1]._edge_hexnode_array.put(Direction.NORTH,
// // this.hexnode_array_2d[i][j]);
// // }
// // }
// // }
// // }
// //
// // public boolean AddHex(final int grid_x, final int grid_y, HEX hex)
// // {
// // // We do not support adding NULL Hexes
// // if (hex == null)
// // return false;
// //
// // if ((grid_x < this.min_grid_x) || (grid_x >= this.min_grid_x +
// // this.grid_width))
// // return false;
// //
// // if ((grid_y < this.min_grid_y) || (grid_y >= this.min_grid_y +
// // this.grid_height))
// // return false;
// //
// // _hexnode_array_2d[grid_x - this.min_grid_x][grid_y -
// // this.min_grid_y]._hex = hex;
// // _hex_hexnode_map.put(hex, this.hexnode_array_2d[grid_x -
// // this.min_grid_x][grid_y - this.min_grid_y]);
// //
// // return true;
// // }
// //
// // public HEX getHex(final int grid_x, final int grid_y)
// // {
// // if ((grid_x < this.min_grid_x) || (grid_x >= (long)(this.min_grid_x +
// // this.grid_width)))
// // return null;
// //
// // if ((grid_y < this.min_grid_y) || (grid_y >= (long)(this.min_grid_y +
// // this.grid_height)))
// // return null;
// //
// // return this.hexnode_array_2d[grid_x - this.min_grid_x][grid_y -
// // this.min_grid_y]._hex;
// // }
// //
// // public HEX getHexInDirection(HEX start_hex, final Hex.Direction
// // direction)
// // {
// // Hex.Grid<HEX>.Node start_node = this.hex_hexnode_map.get(start_hex);
// // if (start_node != null)
// // {
// // Hex.Grid<HEX>.Node end_node =
// // start_node._edge_hexnode_array.get(direction);
// // if (end_node != null)
// // return end_node._hex;
// // }
// // return null;
// // }
// //
// // public Hex.Position getHexPosition(HEX hex)
// // {
// // Hex.Grid<HEX>.Node node = this.hex_hexnode_map.get(hex);
// // if (node != null)
// // {
// // return new Hex.Position(node.getGridX(), node.getGridY());
// // }
// // return null;
// // }
// //
// // private class Node
// // {
// // final int grid_x;
// // final int grid_y;
// //
// // Node(final int grid_x, final int grid_y)
// // {
// // grid_x = grid_x;
// // grid_y = grid_y;
// // _hex = null;
// // _edge_hexnode_array = new HashMap<Hex.Direction,
// // Hex.Grid<HEX>.Node>(Hex.Direction.values().length);
// // // TODO: I SHOULD null out this.edge_hexnode_array[*], but we immediately
// // populate it in the constructor...
// // }
// //
// // //private Hex.IHex this.ihex;
// // private HEX this.hex;
// // private final Map<Hex.Direction, Hex.Grid<HEX>.Node>
// this.edge_hexnode_array;
// // //Hex::HexGrid::HexNode *_edge_hexnode_array[Hex::DIRECTION_SIZE];
// // }
// //
// // private final int this.min_grid_x;
// // private final int this.min_grid_y;
// // private final int this.grid_width;
// // private final int this.grid_height;
// // private final Map<HEX, Hex.Grid<HEX>.Node> this.hex_hexnode_map;
// // private final Hex.Grid<HEX>.Node this.hexnode_array_2d[][];
// // }
// //
// // */
